alpha-mind的portfolio文件夹提供了构建组合的工具函数。

  • 不同的构建方式实质上对应了不同的优化问题。
  • 该文件夹报告的构建方式包括线性构建、均值方差构建等。

线性构建: linear_builder

  • 目标: 在线性约束下最大化组合的预期收益率。
  • 线性约束包括
    • 标的权重的上下限 lbound, ubound
    • 组合风险暴露的上下限 risk_target : 函数内部会以因子矩阵乘以权重得到组合的风险暴露。
    • 仓位调整的换手率上限(换手率以目标权重和当前权重向量的一范数表示,即差的绝对值之和) turn_over_target

In [1]:
import numpy as np
from alphamind.portfolio.linearbuilder import linear_builder

"""
问题初始化
"""

# 假设有5个标的
n = 5

# 假设当前标的的权重
current_pos = np.random.randint(0, n, size=n)
current_pos = current_pos / current_pos.sum() 

# 假设标的预期收益向量
expect_return = np.random.randn(n)

# 假设标的风险因子矩阵, 仅有一个风险因子
risk_factors = np.ones((n, 1))

# 约束条件
# 约束条件1 - 标的权重的上下限
weight_lb = np.zeros(n)
weight_ub = 0.5 * np.ones(n)

# 限制条件2 - 组合风险暴露的上下限
risk_lbound = np.ones(1)
risk_ubound = np.ones(1)

# 限制条件3 - 仓位调整的换手率上限(下限为0,故无需设置)
turn_over_target = 0.1

"""
问题求解
"""
status, fvalue, x_values = linear_builder(expect_return,
                                          weight_lb,
                                          weight_ub,
                                          risk_factors,
                                          (risk_lbound, risk_ubound),
                                          turn_over_target,
                                          current_pos,
                                          method='ecos')


print('Optimization status - {}'.format(status))
print('Optimal expect return - {}'.format(fvalue))
print('Optimial portfolio weights - {}'.format(x_values))
print('Initial portfolio weights - {}'.format(current_pos))
print('Turn over amount - {}'.format(np.abs(x_values - current_pos).sum()))


Optimization status - optimal
Optimal expect return - -0.18504428442588658
Optimial portfolio weights - [0.05 0.05 0.3  0.3  0.3 ]
Initial portfolio weights - [0.  0.1 0.3 0.3 0.3]
Turn over amount - 0.1000000000000593

均值方差构建: mean_variance_builder

  • 根据经典的均值方差组合优化问题构建模型。
  • 目标函数为
\begin{align*} &\mathop{\arg\max}_{\omega} \ \ \omega_a R - \frac{1}{2} \lambda \sigma \\ &s.t.\quad \begin{cases} \omega_L \le \omega_a \le \omega_U \\ \sigma_L \le \sigma \le \sigma_U \end{cases} \end{align*}

其中

  • $\omega_a$是组合的主动权重向量,如果用$\omega$和$\omega_b$表示组合标的绝对权重向量和基准指数的标的权重向量,那么$\omega_a =\omega -\omega_b$。
  • $R$是组合标的预期收益率向量。
  • $\lambda$是风险厌恶系数, $\sigma$是整个组合收益率的方差。
函数的参数说明和使用

mean_variance_builder 接受的参数有

  • 组合标的的预期收益率向量 er
  • 风险模型 risk_model (optional)
  • 基准指数的标的权重向量bm
  • 组合标的的权重上下限 lbound, ubound
  • 组合风险暴露矩阵 risk_exposure (optional)
  • 组合风险暴露的上下限 risk_target (optional)
  • 风险厌恶系数 lam (optional)

NOTE 1: 组合标的的权重上下限 lbound, ubound 是指绝对权重向量的上下限。

  • 代码中会计算主动权重上下限,即 $\omega_L$=lbound - $\omega_b$, $\omega_U$=ubound - $\omega_b$。最后会把$\omega_b$加回到优化问题的解,得到组合标的最优权重。
  • 代码中也会计算主动收益的风险上下限, 即risk_target_lbound - risk_exposure $\omega_b$, risk_target_ubound + risk_exposure $\omega_b$

NOTE 2: 为了计算组合的方差,mean_variance_builder接受两种输入参数:

  • 标的收益率的协方差矩阵$\Sigma$,由此可以计算得到
\begin{equation*} \sigma = \omega_a^T\Sigma \omega_a \end{equation*}

其中$\Sigma$对应入参中的risk_model['cov']。

  • 风险模型:因子收益率的协方差矩阵$F$, 特质收益率的协方差矩阵$\Delta$(对角矩阵) \begin{equation*} \sigma = \omega_a^T \left(X F X^T + \Delta \right) \omega_a \end{equation*}

由于一般情况下风险因子的数量小于标的股票的数量,所以$\omega_a^T X F X^T \omega_a$的时间复杂度要小于$\omega_a^T\Sigma \omega_a$, 所以第二种方法效率更高。

其中, $F$对应入参中的risk_model['factor_cov'], $\Delta$对应risk_model['idsync'],$X$对应risk_model['factor_loading']


In [2]:
import numpy as np
import pandas as pd
from alphamind.portfolio.meanvariancebuilder import mean_variance_builder

"""
问题初始化
"""
# 假设有3个标的
# 假设标的预期收益向量
expect_return = np.array([0.1, 0.2, 0.3])

# 假设收益的协方差矩阵
cov = np.array([[0.02, 0.01, 0.02],
                [0.01, 0.02, 0.03],
                [0.02, 0.03, 0.02]])

# 假设收益的非系统性方差(Idiosyncratic Risk)
ids_var = np.diag([0.01, 0.02, 0.03])

# 最终的协方差矩阵为
cov += ids_var

# 假设标的风险暴露矩阵, 有两个风险因子
risk_exposure = np.array([[1., 1., 1.],
                         [1., 0., 1.]]).T

# 假设基准指数的权重向量
bm = np.array([0.3, 0.3, 0.4])

# 约束条件
# 约束条件1 - 标的绝对权重的上下限
lbound = np.array([0., 0., 0.])
ubound = np.array([0.4, 0.4, 0.5])

# 约束条件2 - 组合风险暴露的上下限
risk_target = (np.array([bm.sum(), 0.3]), np.array([bm.sum(), 0.7]))

# 建立风险模型,假设直接使用标的的协方差矩阵计算组合的方差
risk_model = dict(cov=cov, factor_cov=None, factor_loading=None, idsync=None)

"""
问题求解
"""
status, _, x = mean_variance_builder(expect_return, risk_model, bm, lbound, ubound, risk_exposure, risk_target)

In [3]:
print('Optimal weight is {0}'.format(x))
print('Risk exposure of optimal potfolio is {0}'.format(x @ risk_exposure))


Optimal weight is [0.09999998 0.4        0.5       ]
Risk exposure of optimal potfolio is [0.99999998 0.59999998]

In [ ]: